////////////////////////////////////////////////
// DESCRIPTION:
//    Support for retrieving Version information
//    from the firmware
//
// Legal Notices:
//   Copyright (c) 2008, Telliam Consulting, LLC.
//   All rights reserved.
//
//   Redistribution and use in source and binary forms, with or without modification,
//   are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice, this list
//     of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice, this
//     list of conditions and the following disclaimer in the documentation and/or other
//     materials provided with the distribution.
//   * Neither the name of Telliam Consulting nor the names of its contributors may be
//     used to endorse or promote products derived from this software without specific
//     prior written permission.
//
//   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
//   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
//   SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
//   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
//   BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
//   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
//   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
//   DAMAGE. 
//
using System;
using Microsoft.SPOT;
using System.Reflection;
using System.Globalization;
using FusionWare.SPOT.Native;
using System.Runtime.InteropServices;
using FusionWare.SPOT.Runtime.Serialization;

namespace FusionWare.SPOT.EmulatorComponents
{
    /// <summary>Provides HAL Version information to the applications via a Virtual serial port</summary>
    /// <remarks>
    /// This class is subject to change and or removal at any time.
    /// OEMS should NOT rely on the implementation details or existence 
    /// of this class as it will be removed in future versions of the
    /// libraries once a real interop solution exists from Micosoft.
    /// 
    /// This class supports creatin and initialization of the version number
    /// from the emulator XML configuration file. This allows testing of the
    /// application code to handle version diferences with a simple change 
    /// in the version settings in the config file rather than a complete
    /// re-build of the emulator.
    /// </remarks>
    public class VersionInfo : ComPortComponent
    {
        /// <summary>Class to contain version information</summary>
        /// <remarks>
        /// This must be kept in synch with the
        /// FusionWare.SPOT.VersionInfo structure to make sure
        /// both are using the same field types and ordering
        /// </remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes" ),
        Serializable, StructLayout( LayoutKind.Sequential )]
        private struct VersionInfoData
        {
            /// <summary>Major part of the version number</summary>
            [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields" )]
            public uint Major;
            /// <summary>Minor part of the version number</summary>
            [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields" )]
            public uint Minor;
            /// <summary>Build number part of the version number</summary>
            [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields" )]
            public uint Build;
            /// <summary>Revision part of the version number</summary>
            [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields" )]
            public uint Revision;

            /// <summary>Creates a new version info data structure from a string</summary>
            /// <param name="VerStr"></param>
            public VersionInfoData( string VerStr )
            {
                if( string.IsNullOrEmpty( VerStr ) )
                    throw new ArgumentNullException( "VerStr" );

                string[] split = VerStr.Split( new char[ ] { '.' } );
                this.Major = uint.Parse( split[ 0 ], NumberStyles.None );
                this.Minor = uint.Parse( split[ 1 ], NumberStyles.None );
                this.Build = uint.Parse( split[ 2 ], NumberStyles.None );
                this.Revision = uint.Parse( split[ 3 ], NumberStyles.None );
            }

            /// <summary>Converts a Version Info to an array of bytes for marshalling</summary>
            /// <returns>VersionInfo data structure marshalled as an array of bytes</returns>
            public byte[ ] ToArray( )
            {
                return BinarySerializer.Serialize( this );
            }
        }

        VersionInfoData verFirmware;
        byte[] data;

        /// <summary>Major version number</summary>
        public uint Major
        {
            get { return verFirmware.Major; }
            set
            {
                base.ThrowIfNotConfigurable( );
                verFirmware.Major = value;
            }
        }
        
        /// <summary>Minor Version number</summary>
        public uint Minor
        {
            get { return verFirmware.Minor; }
            set
            {
                base.ThrowIfNotConfigurable( );
                verFirmware.Minor = value;
            }
        }

        /// <summary>Build number</summary>
        public uint Build
        {
            get { return verFirmware.Build; }
            set
            {
                base.ThrowIfNotConfigurable( );
                verFirmware.Build = value;
            }
        }

        /// <summary>Revision number</summary>
        public uint Revision
        {
            get { return verFirmware.Revision; }
            set
            {
                base.ThrowIfNotConfigurable( );
                this.verFirmware.Revision = value;
            }
        }

        /// <summary>Performs post configuration setup</summary>
        public override void SetupComponent( )
        {
            base.SetupComponent( );
            data = this.verFirmware.ToArray( );
        }

        /// <summary>Initializes a VersionInfo component with default field values</summary>
        /// <remarks>
        /// This constructor is required to create an instance from the config file
        /// </remarks>
        // 16 byte read FIFO and 1 byte write FIFO is all that is needed for the version info
        public VersionInfo()
            : base( (int)VPortIDs.VPORT_DEV_VERSION, 16, 1 )
        {
            // By default grab version info from *THIS* assembly and use it
            string asmVer = Assembly.GetAssembly( typeof( VersionInfo ) ).GetName().Version.ToString();
            this.verFirmware = new VersionInfoData( asmVer.ToString() );
        }

        /// <summary>Flush resets the data to the beginning of the structure</summary>
        protected override void OnFlush()
        {
            base.DeviceViewStream.Flush();
            base.DeviceViewStream.Write(data, 0, data.Length);
        }
    }
}
